
export default class SeatCalculator {
  constructor(seatList, options) {
    this.seatList = seatList;
    this.options = options;
    this._buildSeatArray();
    this._buildIndexMap();
  }

  /* ========== 基础数据结构 ========== */
  _buildSeatArray() {
    const { ROW_ID, COLUMN_ID } = this.options.SEAT_FIELDS;
    const arr = [];
    this.seatList.forEach(s => {
      const r = Number(s[ROW_ID]);
      const c = Number(s[COLUMN_ID]);
      if (!arr[r]) arr[r] = [];
      arr[r][c] = s;
    });
    this.seatArray = arr;
    this.seatRow = arr.length;
    this.seatCol = Math.max(...arr.map(r => r.length).filter(Boolean), 1);
    
  }

  _buildIndexMap() {
    this.seatMap = new Map();
    const { ROW_ID, COLUMN_ID } = this.options.SEAT_FIELDS;
    this.seatList.forEach((s, idx) =>
      this.seatMap.set(`${s[ROW_ID]}-${s[COLUMN_ID]}`, idx)
    );
  }

  /* ========== 尺寸与边界 ========== */
  calcCellSize(canvasWidth) {
    const { seatMinWidth, seatMaxWidth } = this.options;
    const raw = (canvasWidth) / this.seatCol;
    return Math.max(seatMinWidth, Math.min(raw, seatMaxWidth));
  }
  calcSeatRowCol() {
    return {
        seatRow: this.seatRow,
        seatCol: this.seatCol,
    };
  }
  calcInitialPan(canvasWidth, canvasHeight, cellSize) {
    const { hallHeight } = this.options;
    return {
      panX: (canvasWidth - cellSize * this.seatCol) / 2,
      panY: hallHeight,
    };
  }

  getBoundaries(canvasWidth, canvasHeight, footerHeight, cellSize) {
    const { hallHeight } = this.options;
    const contentW = cellSize * this.seatCol;
    const contentH = cellSize * this.seatRow;
    const availH = canvasHeight - hallHeight - footerHeight;

    return {
      maxOffsetX: contentW < canvasWidth ? canvasWidth - contentW : 0,
      minOffsetX: contentW > canvasWidth ? canvasWidth - contentW : 0,
      maxOffsetY: hallHeight,
      minOffsetY: contentH > availH ? availH - contentH : 0,
    };
  }

  clampOffset(panX, panY, boundaries) {
    const { maxOffsetX, minOffsetX, maxOffsetY, minOffsetY } = boundaries;
    return {
      targetX: Math.max(minOffsetX, Math.min(maxOffsetX, panX)),
      targetY: Math.max(minOffsetY, Math.min(maxOffsetY, panY)),
    };
  }

  /* ========== 缩放 ========== */
  calcPinchZoom({oldCellSize,scale,centerX,centerY,panX,panY,seatCol,canvasWidth,seatMaxWidth,seatMinWidth}) {
    const newSize = oldCellSize * scale;
    if (newSize < (canvasWidth) / seatCol || newSize > seatMaxWidth)
      return null; // 越界

    const oldContentCX = ((centerX - panX) / oldCellSize);
    const oldContentCY = ((centerY - panY) / oldCellSize);

    return {
      cellSize: newSize,
      panX: centerX - oldContentCX * newSize,
      panY: centerY - oldContentCY * newSize,
    };
  }

  /* ========== 获取与给定座位相关的所有座位索引 ========== */
  getRelatedSeatIndices(seat) {
    const { SINGLE, COUPLE_LEFT, COUPLE_RIGHT } = this.options.SEAT_TYPE;
    const { TYPE_NAME, ROW_ID, COLUMN_ID } = this.options.SEAT_FIELDS;
    const list = [this.seatMap.get(`${seat[ROW_ID]}-${seat[COLUMN_ID]}`)];
    if (seat[TYPE_NAME] === SINGLE) return list;

    const offset = seat[TYPE_NAME] === COUPLE_LEFT ? 1 : -1;
    const partnerKey = `${seat[ROW_ID]}-${Number(seat[COLUMN_ID]) + offset}`;
    if (this.seatMap.has(partnerKey)) {
      const partnerSeat = this.seatList[this.seatMap.get(partnerKey)];
      if (partnerSeat[TYPE_NAME] !== SINGLE)
        list.push(this.seatMap.get(partnerKey));
    }
    return list;
  }

  /* ========== 更新座位状态 ========== */
  updateSeatStatus(targetStatus, indices) {
    const { STATUS_NAME } = this.options.SEAT_FIELDS;
    indices.forEach(idx => {
      this.seatList[idx][STATUS_NAME] = targetStatus;
      this.seatList[idx].clickTime = Date.now();
    });
  }

  cancelSeat(seat) {
    const { AVAILABLE } = this.options.SEAT_STATUS;
    const { STATUS_NAME } = this.options.SEAT_FIELDS;
    this.getRelatedSeatIndices(seat).forEach(idx => {
      this.seatList[idx][STATUS_NAME] = AVAILABLE;
    });
  }

  /* ========== 验票 ========== */
  validateSeat(seat) {
    const { ROW_ID, COLUMN_ID, STATUS_NAME} = this.options.SEAT_FIELDS;
    const row = this.seatArray[seat[ROW_ID]];
    if (!row) return 0;
    const col = Number(seat[COLUMN_ID]);
    const { AVAILABLE, SELECTED } = this.options.SEAT_STATUS;

    let err = 0;
    const prev = [], next = [];
    // 左侧连续空位
    for (let c = col - 1; c >= 0; c--) {
      const s = row[c];
      if (!s || ![AVAILABLE, SELECTED].includes(s[STATUS_NAME])) break;
      if (s[STATUS_NAME] === AVAILABLE) prev.push(s);
    }
    // 右侧连续空位
    for (let c = col + 1; c < row.length; c++) {
      const s = row[c];
      if (!s || ![AVAILABLE, SELECTED].includes(s[STATUS_NAME])) break;
      if (s[STATUS_NAME] === AVAILABLE) next.push(s);
    }
    if (!((prev.length === 0 && next.length === 0) || (prev.length >= 2 && next.length >= 2))) {
      if (prev.length === 1 && next.length !== 0) err++;
      if (next.length === 1 && prev.length !== 0) err++;
    }
    // 相邻已选中间夹一个空位
    row.forEach((s, i) => {
      if (s?.[STATUS_NAME] === SELECTED &&
          row[i + 1]?.[STATUS_NAME] === AVAILABLE &&
          row[i + 2]?.[STATUS_NAME] === SELECTED) err++;
    });
    return err;
  }


  /* ---------- 坐标转换 ---------- */
  clientToCanvas({x, y,panX, panY}) {
    const canvasX = x - panX;
    const canvasY = y - panY;
    return { canvasX , canvasY };
  }
  /* ---------- 距离计算 ---------- */
  calcDistance(t1, t2) {
    return Math.hypot(t1.x - t2.x, t1.y - t2.y);
  }

}